﻿////////////////////////////////////////////////////////////////////////////////////////////
// File: Mouse.cs                                                                         //
// Author: Mike Binnix                                                                    //
// Desc: Class representing a computer mouse.  Wraps the XNA input methods for            //
//             mice into a single class to more easily check a users input.               //
////////////////////////////////////////////////////////////////////////////////////////////

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace SlothEngine.Input
{
    public class XNAMouse : IInputDevice
    {
        private MouseState currentMouseState;
        private MouseState previousMouseState;
        private Vector2 position;
        private int previousWheelValue;
        private int currentWheelValue;
        private bool mouseInitialized;
        private PlayerID player;

        public Vector2 Position 
        {
            get
            {
                return position;
            }
            set
            {
                position = value;
            }
        }
        public int X
        {
            get
            {
                return (int)position.X;
            }
            set
            {
                position.X = value;
            }
        }
        public int Y
        {
            get
            {
                return (int)position.Y;
            }
            set
            {
                position.Y = value;
            }
        }
        public bool Initialized 
        { 
            get
            {
                return mouseInitialized;
            }
        }
        public PlayerID Player
        {
            get
            {
                return player;
            }
            set
            {
                if (value == PlayerID.All)
                    throw new Exception("Can not assign PlayerID to PlayerID." + value.ToString());
                else
                    player = value;
            }
        }

        public XNAMouse()
        {
            currentMouseState = new MouseState();
            previousMouseState = new MouseState();
            position = new Vector2(Microsoft.Xna.Framework.Input.Mouse.GetState().X, Microsoft.Xna.Framework.Input.Mouse.GetState().Y);
            previousWheelValue = previousMouseState.ScrollWheelValue;
            currentWheelValue = currentMouseState.ScrollWheelValue;
            player = PlayerID.One;

            mouseInitialized = true;
        }
        public XNAMouse(PlayerID player)
        {
            currentMouseState = new MouseState();
            previousMouseState = new MouseState();
            position = new Vector2(Microsoft.Xna.Framework.Input.Mouse.GetState().X, Microsoft.Xna.Framework.Input.Mouse.GetState().Y);
            previousWheelValue = previousMouseState.ScrollWheelValue;
            currentWheelValue = currentMouseState.ScrollWheelValue;

            if (player == PlayerID.All)
                throw new Exception("Can not assign PlayerID to PlayerID." + player.ToString());
            else
                this.player = player;

            mouseInitialized = true;
        }

        /// <summary>
        /// Updates the button states and position of the mouse every frame.
        /// </summary>
        /// <param name="gameTime">Games time</param>
        public void Update(GameTime gameTime)
        {
            previousMouseState = currentMouseState;
            currentMouseState = Microsoft.Xna.Framework.Input.Mouse.GetState();

            position.X = currentMouseState.X;
            position.Y = currentMouseState.Y;

            previousWheelValue = currentWheelValue;
            currentWheelValue = currentMouseState.ScrollWheelValue;
        }
        /// <summary>
        /// Checks the state of the mouse scroll wheel.
        /// </summary>
        /// <param name="state">Scroll state to check against.</param>
        /// <returns>Returns true if the scroll wheel state matches the state to check atainst.  Otherwise returns false.</returns>
        public bool ScrollState(MouseScrollState state)
        {
            switch (state)
            {
                case MouseScrollState.Idle:
                    if (currentWheelValue == previousWheelValue)
                        return true;
                    break;
                case MouseScrollState.ScrollDown:
                    if (currentWheelValue < previousWheelValue)
                        return true;
                    break;
                case MouseScrollState.ScrollUp:
                    if (currentWheelValue > previousWheelValue)
                        return true;
                    break;
            }

            return false;
        }
        /// <summary>
        /// Checks if a mouse button has been pressed on the current frame.
        /// </summary>
        /// <param name="button">Mouse button to check</param>
        /// <returns>Returns true if mouse button has been pressed this frame</returns>
        public bool ButtonPressed(MouseButtons button)
        {
            if (checkButtonState(button, ButtonState.Pressed, currentMouseState) && !checkButtonState(button, ButtonState.Pressed, previousMouseState))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Checks if a mouse button has been released in the current frame.
        /// </summary>
        /// <param name="button">Mouse button to test</param>
        /// <returns>Returns true if mouse button has been released this frame</returns>
        public bool ButtonReleased(MouseButtons button)
        {
            if (checkButtonState(button, ButtonState.Released, currentMouseState) && checkButtonState(button, ButtonState.Pressed, previousMouseState))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Checks if a mouse button has been pressed for multiple frames.
        /// </summary>
        /// <param name="button">Button to test</param>
        /// <returns>Returns true if mouse button was pressed this frame as well as the previous frame.</returns>
        public bool ButtonHeld(MouseButtons button)
        {
            if (checkButtonState(button, ButtonState.Pressed, currentMouseState) && checkButtonState(button, ButtonState.Pressed, previousMouseState))
                return true;
            else
                return false;
        }
        /// <summary>
        /// Checks if the state of a button is pressed or released.
        /// </summary>
        /// <param name="button">Mouse button to test.</param>
        /// <param name="state">The state of the button to test for.</param>
        /// <param name="refState">The state list to check against.</param>
        /// <returns>Returns true if the button within the button list has the given state.</returns>
        private static bool checkButtonState(MouseButtons button, ButtonState state, MouseState refState)
        {
            switch (button)
            {
                case MouseButtons.LeftButton:
                    if (refState.LeftButton == state)
                        return true;
                    break;
                case MouseButtons.RightButton:
                    if (refState.RightButton == state)
                        return true;
                    break;
                case MouseButtons.MiddleButton:
                    if (refState.MiddleButton == state)
                        return true;
                    break;
                case MouseButtons.Button1:
                    if (refState.XButton1 == state)
                        return true;
                    break;
                case MouseButtons.Button2:
                    if (refState.XButton2 == state)
                        return true;
                    break;
            }

            return false;
        }
    }
}
